<script setup lang="ts" generic="T">
import { ref } from "vue";
import { ExcelReader } from "@chulei-ui/utils";
import { ElNotification, UPDATE_MODEL_EVENT } from "@chulei-ui/element-plus";
import { parse } from "csv-string";
import { Check, Delete } from "@element-plus/icons-vue";
import type {
  UploadFile,
  UploadFiles,
  UploadInstance,
  UploadRawFile,
  UploadRequestOptions,
} from "@chulei-ui/element-plus";

type ExcelData<T> = {
  name: string;
  rows: T[];
};

const uploadRef = ref<UploadInstance>();

const props = withDefaults(
  defineProps<{
    modelValue: T[];
    columns: { field: string; title: string }[];
    size?: "large" | "default" | "small";
  }>(),
  {
    size: "default",
  }
);

const files = ref<UploadFiles>([]);
const emits = defineEmits([UPDATE_MODEL_EVENT, "finish"]);

const readCsv = async (file: File) => {
  const csvText = await file.text();
  const rows = parse(csvText);
  const header: string[] = rows[0];
  const headerIndexes = header.reduce((columnIndexes, title) => {
    const columnIndex = props.columns.findIndex((column) => column.title === title);
    const column = props.columns[columnIndex];
    if (column?.field) {
      columnIndexes[columnIndex] = column.field as string;
    }
    return columnIndexes;
  }, {} as Record<number, string>);
  return rows.slice(1, rows.length).map((row) => {
    return row.reduce((record, value, currentIndex) => {
      record[headerIndexes[currentIndex]] = value;
      return record;
    }, {} as Record<string, string>) as T;
  });
};

const readXlsx = async (file: File) => {
  const reader = await ExcelReader.read<T>(file);
  return reader.mapObject(props.columns);
};

const handleImportExcel = async (options: UploadRequestOptions) => {
  const file = options.file;
  const data = file.name.endsWith(".csv") ? await readCsv(file) : await readXlsx(file);
  options.onSuccess({ name: file.name, rows: data });
  return data;
};

const handleImportSuccess = (response: ExcelData<T>, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  files.value = uploadFiles;
  const rows = uploadFiles
    .filter((f) => f.status === "success")
    .flatMap((file) => {
      return file.response;
    });

  emits(UPDATE_MODEL_EVENT, rows);
};

const handleBeforeUpload = (rawFile: UploadRawFile) => {
  if (files.value.some((file) => file.name === rawFile.name)) {
    ElNotification({
      title: "文件导入",
      message: `文件${rawFile.name}已导入请勿重复导入`,
      type: "error",
    });
    return false;
  }
};

const handleRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  files.value = uploadFiles;
  const rows = uploadFiles
    .filter((f) => f.status === "success")
    .flatMap((file) => {
      return file.response;
    });
  emits(UPDATE_MODEL_EVENT, rows);
};

const removeFile = (file: UploadFile) => {
  uploadRef.value?.handleRemove(file);
};

defineOptions({
  name: "ExcelImportButton",
});
</script>

<template>
  <el-upload
    ref="uploadRef"
    class="import-button"
    multiple
    :before-upload="handleBeforeUpload"
    action="''"
    accept=".xls,.xlsx,.csv"
    :http-request="handleImportExcel"
    :show-file-list="false"
    @remove="handleRemove"
    @success="handleImportSuccess"
  >
    <el-button-group :size="size!">
      <slot />
      <el-dropdown v-if="files.length > 0" trigger="click" :hide-on-click="false">
        <el-button @click.stop>{{ files.length }}</el-button>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item v-for="file in files" :key="file.name">
              <div style="display: flex; justify-content: space-between; width: 100%; align-items: center">
                <el-text>{{ file.name }}</el-text>
                <span>
                  <el-divider direction="vertical" />
                  <el-icon style="color: var(--el-color-success); cursor: default"><Check /></el-icon>
                  <el-icon @click="() => removeFile(file)"><Delete /></el-icon>
                </span>
              </div>
            </el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </el-button-group>
  </el-upload>
</template>

<style scoped lang="scss">
.import-button {
  ::v-deep(.el-upload) {
    display: flex;
  }
}
</style>
